
#include <stdint.h>
#include <string.h>
#include <ICC/Connector.h>
#include <ICC/Slot.h>
#include <buf.h>

#include "exception.h"
#include "utils.h"
#include "asn1.h"
#include "tlv.h"
#include "dispatcher.h"
#include "ipcMessage.h"
#include "Debug.h"
#include "Log.h"

int SamCommand_ExtractData(uint8_t * ptr, uint16_t objectLength, uint8_t tag, uint8_t* pdata, uint8_t* dlen){
	int e = 0;
	
	Try {
		uint16_t clen;
		clen = tlv_search(&ptr, &objectLength, tag);
		if(pdata){
			memcpy(pdata, ptr, clen);
		}
		if(dlen){
			*dlen = clen;
		}
	}
	Catch(e){
	
	}
	
	return e;
}


void SamCommand_Init(struct net_buf_simple* buf){
	net_buf_simple_init(buf, 16);
}

void SamCommand_AddLength(struct net_buf_simple* buf, const uint16_t lenVal){
	if(lenVal < 128){
		net_buf_simple_add_u8(buf, lenVal);
	}
	else{
		net_buf_simple_add_u8(buf, 0x82);
		net_buf_simple_add_u8(buf, (lenVal >> 8));
		net_buf_simple_add_u8(buf, lenVal);
	}
}

void SamCommand_PushLength(struct net_buf_simple* buf, const uint16_t lenVal){
	if(lenVal < 128){
		net_buf_simple_push_u8(buf, lenVal);
	}
	else{
		net_buf_simple_push_u8(buf, lenVal);
		net_buf_simple_push_u8(buf, (lenVal >> 8));
		net_buf_simple_push_u8(buf, 0x82);
	}
}

void SamCommand_AddParam(struct net_buf_simple* buf, uint8_t tag, uint16_t len, const uint8_t* val){
	net_buf_simple_add_u8(buf, tag);
	SamCommand_AddLength(buf, len);
	if(len > 0){
		net_buf_simple_add_mem(buf, val, len);
	}
}

void SamCommand_PushHeadTag(struct net_buf_simple* buf, uint8_t cmd){
	SamCommand_PushLength(buf, buf->len);
	net_buf_simple_push_u8(buf, cmd);
}

void SamCommand_PushSamCmd(struct net_buf_simple* buf){
	const uint8_t sam_cmd = ASN1_CHOICE_CONSTRUCTED | PAYLOAD_SAM_COMMAND;
	SamCommand_PushHeadTag(buf, sam_cmd);
}

int SamCommand_GetVersionInfo(uint8_t *resp, uint8_t* rlen){
	struct net_buf_simple * buf = NET_BUF_SIMPLE(32);
	SamCommand_Init(buf);
	SamCommand_AddParam(buf, ASN1_CHOICE_PRIMITIVE | SAMCOMMAND_GET_VERSION_INFO, 0, NULL);
	SamCommand_PushSamCmd(buf);
	return api_Dispatch(buf->data, buf->len, resp, rlen);
}

int SamCommand_PerformAnticollision(uint8_t protocol, uint8_t *resp, uint8_t* rlen){
	uint8_t cmd[] = {
		0xA0, 0x05, //Tag = PAYLOAD_SAM_COMMAND, Len = 5
		0xB7, 0x03, //Tag = SAMCOMMAND_PERFORM_ANTICOLLISION, Len = 3
		0x80, 0x01, protocol, //Tag = FRAME_PROTOCOL_LIST, Len = 1, Val = PICO15693
	};

	return api_Dispatch(cmd, sizeof(cmd), resp, rlen);
}

int SamCommand_SetDetectedCardInfo(uint8_t protocol, const uint8_t* csn, uint8_t csn_len, const uint8_t* atr, uint8_t alen, const uint8_t* sak,
	uint8_t* resp, uint8_t *rlen){
	
	struct net_buf_simple * buf = NET_BUF_SIMPLE(64);
	
	SamCommand_Init(buf);
	//Protocol
	{
	uint8_t val[2] = {0x00, protocol};
	SamCommand_AddParam(buf, 0x80, sizeof(val), val);
	}
	//CSN
	SamCommand_AddParam(buf, 0x81, csn_len, csn);
	//atr
	if(alen > 0){
		SamCommand_AddParam(buf, 0x82, alen, atr);
	}
	
	if(sak){
		SamCommand_AddParam(buf, 0x83, sizeof(*sak), sak);
	}
	
	SamCommand_AddParam(buf, 0x00, 0, NULL);
	//SetDetectedCardInfo
	SamCommand_PushHeadTag(buf, 0xAD);
	SamCommand_PushSamCmd(buf);
	
	return api_Dispatch(buf->data, buf->len, resp, rlen);
}


int SamCommand_SetDetectedCardInfo2(uint8_t protocol, const uint8_t* csn, uint8_t csn_len, 
	const uint8_t* atr, uint8_t alen, const uint8_t* sak, const uint8_t* do_tcl, uint8_t** resp, uint8_t *rlen){
	
	struct net_buf_simple * buf = NET_BUF_SIMPLE(64);
	
	SamCommand_Init(buf);
	//Protocol
	{
	uint8_t val[2] = {0x00, protocol};
	SamCommand_AddParam(buf, 0x80, sizeof(val), val);
	}
	//CSN
	SamCommand_AddParam(buf, 0x81, csn_len, csn);
	//atr
	if(alen > 0){
		SamCommand_AddParam(buf, 0x82, alen, atr);
	}
	//sak
	if(sak){
		SamCommand_AddParam(buf, 0x83, sizeof(*sak), sak);
	}
	// DoTCL flag byte
	if(do_tcl){
		SamCommand_AddParam(buf, 0x87, sizeof(*do_tcl), do_tcl);
	}
	//SetDetectedCardInfo
	SamCommand_PushHeadTag(buf, 0xA0);
	SamCommand_PushHeadTag(buf, 0xAD);
	SamCommand_PushSamCmd(buf);
	uint16_t len = 0;
	int ret = api_Dispatch2(0, buf->data, buf->len, resp, &len);
	if(!ret){
		*rlen = len;
	}
	return ret;
}	

int SamCommand_GetContentElement2(uint8_t** resp, uint8_t *rlen){
	uint8_t cmd[] = {
		0xA0, 0x05,			//SamCommand
		0xBE, 0x03,			//GetContentElement2
		0x80, 0x01, 0x04,			//ContentElementTag, Tag of Content Element to be read 
	};
	uint16_t len = 0;
	int ret = api_Dispatch2(0, cmd, sizeof(cmd), resp, &len);
	if(!ret){
		*rlen = len;
	}
	return ret;
}

int SamCommand_ProcessKeyRollerCard(void){
	uint8_t cmd[] = {
		0xA0,0x03,
		0x9F,0x26, 0x00
	};
	uint8_t *resp;
	uint16_t rlen;
	LOG_I("ProcessKeyRollerCard");
	return api_Dispatch2(0, cmd, sizeof(cmd), &resp, &rlen);
}

int SamCommand_ProcessConfigCard(void){
	uint8_t cmd[] = {
		0xA0,0x02,
		0x9A,0x00
	};
	uint8_t *resp;
	uint16_t rlen;
	LOG_I("ProcessConfigCard");
	return api_Dispatch2(1, cmd, sizeof(cmd), &resp, &rlen);
}

int SamCommand_QueryECP(uint8_t** resp, uint16_t *rlen){
	uint8_t cmd[] = {
		0xa0, 
		0x82, 0x00, 0x03, 
		0x9f, 0x3e, 0x00, 
		0x00, 0x00
	};
	LOG_I("QueryECP");
	return api_Dispatch2(0, cmd, sizeof(cmd), resp, rlen);
}

int SamCommand_LoadKey(uint8_t IsPersistent, const uint8_t keyOID[3], uint8_t keyLen, const uint8_t key[], 
	const uint8_t* rights, const uint8_t* role){
	struct net_buf_simple * buf = NET_BUF_SIMPLE(64);
	uint8_t *resp;
	uint16_t rlen;
	TRACE_I("enter");
	
	SamCommand_Init(buf);
	//ADF
	SamCommand_AddParam(buf, 0x80, 1, &IsPersistent);
	//KeyOid
	SamCommand_AddParam(buf, 0x81, 3, keyOID);
	//KeyValue
	SamCommand_AddParam(buf, 0x82, keyLen, key);
	//ScAccessRights
	if(rights){
		SamCommand_AddParam(buf, 0x83, 1, rights);
	}
	//referenceDataQualifier
	if(role){
		SamCommand_AddParam(buf, 0x84, 1, role);
	}
	//Header
	SamCommand_PushHeadTag(buf, 0xA5);
	SamCommand_PushHeadTag(buf, 0xA5);
	SamCommand_PushHeadTag(buf, 0xA0);
	return api_Dispatch2(0, buf->data, buf->len, &resp, &rlen);
}

/*==============================Seos========================================*/

int SamCommand_Seos_Select(void){
	uint8_t cmd[] = {
		0xA0, 0x06, 
		0xA5, 0x04, 
		0xA6, 0x02, 
		0x80, 0x00
	};
	uint8_t *resp;
	uint16_t rlen;
	TRACE_I("enter");
	return api_Dispatch2(0, cmd, sizeof(cmd), &resp, &rlen);
}

int SamCommand_Seos_Select2(uint8_t **resp, uint16_t* rlen){
	uint8_t cmd[] = {
		0xA0, 0x06, 
		0xA5, 0x04, 
		0xA6, 0x02, 
		0x80, 0x00
	};

	TRACE_I("enter");
	return api_Dispatch2(0, cmd, sizeof(cmd), resp, rlen);
}

int SamCommand_Seos_GetLastDiversifier(uint8_t** resp, uint16_t *rlen){
	uint8_t cmd[] = {
		0xA0, 0x06, 
		0xA5, 0x04, 
		0xA6, 0x02, 
		0x85, 0x00
	};
	LOG_I("enter");
	return api_Dispatch2(0, cmd, sizeof(cmd), resp, rlen);
}

int SamCommand_Seos_GetAlgoInfo(uint8_t** resp, uint16_t *rlen){
	uint8_t cmd[] = {
		0xA0, 0x06, 
		0xA5, 0x04, 
		0xA6, 0x02, 
		0x89, 0x00
	};
	LOG_I("enter");
	return api_Dispatch2(0, cmd, sizeof(cmd), resp, rlen);
}

int SamCommand_Seos_Authenticate(const uint8_t adf[], uint8_t adfLen, 
	const uint8_t privEncKeyOid[3], const uint8_t privMacKeyOid[3], const uint8_t authKeyOid[3]){
	struct net_buf_simple * buf = NET_BUF_SIMPLE(64);
	uint8_t *resp;
	uint16_t rlen;
	TRACE_I("enter");
	
	SamCommand_Init(buf);
	//ADF
	SamCommand_AddParam(buf, 0x80, adfLen, adf);
	//privEncKeyOid
	SamCommand_AddParam(buf, 0x81, 3, privEncKeyOid);
	//privMacKeyOid
	SamCommand_AddParam(buf, 0x82, 3, privMacKeyOid);
	//authKeyOid
	SamCommand_AddParam(buf, 0x83, 3, authKeyOid);
	//referenceDataQualifier
	SamCommand_AddParam(buf, 0x84, 1, (uint8_t []){1});
	//Header
	SamCommand_PushHeadTag(buf, 0x30);
	SamCommand_PushHeadTag(buf, 0xA1);
	SamCommand_PushHeadTag(buf, 0xA6);
	SamCommand_PushHeadTag(buf, 0xA5);
	SamCommand_PushHeadTag(buf, 0xA0);
	return api_Dispatch2(0, buf->data, buf->len, &resp, &rlen);
}

int SamCommand_Seos_GetData(const uint8_t tag[], uint8_t tagLen, uint8_t readLen, uint8_t** resp, uint16_t *rlen){
	struct net_buf_simple * buf = NET_BUF_SIMPLE(64);
	TRACE_I("enter");
	
	SamCommand_Init(buf);
	
	//ObjectTag
	SamCommand_AddParam(buf, 0x80, tagLen, tag);
	//ReadLength
	if(readLen){
		SamCommand_AddParam(buf, 0x81, sizeof(readLen), &readLen);
	}

	//Header
	SamCommand_PushHeadTag(buf, 0xA2);
	SamCommand_PushHeadTag(buf, 0xA6);
	SamCommand_PushHeadTag(buf, 0xA5);
	SamCommand_PushHeadTag(buf, 0xA0);
	return api_Dispatch2(0, buf->data, buf->len, resp, rlen);
}

int SamCommand_Seos_PutData(const uint8_t tag[], uint8_t tagLen, uint8_t writeLen, const uint8_t* data){
	struct net_buf_simple * buf = NET_BUF_SIMPLE((256+64));
	uint8_t *resp;
	uint16_t rlen;
	TRACE_I("enter");
	
	SamCommand_Init(buf);
	
	//ObjectTag
	SamCommand_AddParam(buf, 0x80, tagLen, tag);
	//Write Data
	SamCommand_AddParam(buf, 0x81, writeLen, data);

	//Header
	SamCommand_PushHeadTag(buf, 0xA3);
	SamCommand_PushHeadTag(buf, 0xA6);
	SamCommand_PushHeadTag(buf, 0xA5);
	SamCommand_PushHeadTag(buf, 0xA0);
	return api_Dispatch2(0, buf->data, buf->len, &resp, &rlen);
}

/*==========================================================================*/

